#include "../src/nominal.hh"

#include <cassert>
#include <iostream>

namespace
{
using namespace playerpiano;

struct LogPRatingTestDatum 
{ 
  struct
    {
      unsigned int      rating;
      float             alpha[4];
      float             logbeta;
    }                                   inputs;
  struct
    {
      float             logp;
      float             dlogbeta;
      float             ddlogbeta;
    }                                   outputs;
};

void
test_logprating (void)
{
  LogPRatingTestDatum data[] = 
    {{{3, {0.947375, 0.298482, 0.689616, 0.114157},  0.497138}, {-2.1248, -1.2957, -2.16913}}, {{3, {0.157952, 0.30186,  0.131109, 0.119375},  0.623508}, {-2.37582, -1.54164, -2.44303}}, {{3, {0.698482,  0.935676, 0.412585, 0.894346},  0.00205476}, {-2.32768, -1.30794, -1.97954}}, {{3, {0.00397272,  0.385424, 0.917928, 0.54418},  0.120771}, {-2.14747, -1.15479, -1.86415}}, {{2, {0.0836416,  0.271459, 0.0878399, 0.283715},  0.136266}, {-1.8063, -0.654887, -1.12657}}, {{1, {0.972976,  0.398223, 0.169558, 0.639128},  0.815024}, {-3.28846, -2.7956, -3.76952}}, {{3, {0.0963632,  0.0384496, 0.519753, 0.191516},  0.397881}, {-2.17047, -1.19137, -1.91213}}, {{0, {0.102774,  0.107168, 0.29717, 0.395826}, 0.0988009}, {-0.558285, 0.590566,  0.116358}}, {{1, {0.721744, 0.379243, 0.851646, 0.97803},  0.638103}, {-2.73047, -2.26855, -3.21746}}, {{0, {0.107784,  0.763806, 0.694315, 0.501836}, 0.134807}, {-0.375739,  0.587409, -0.174458}}};

  for (unsigned int i = 0; i < sizeof (data) / sizeof (data[0]); ++i)
    {
      Nominal nominal (data[i].inputs.alpha, 0, 0, 1, 4);
      Rating rating = { 0, data[i].inputs.rating };
      float dlogbeta = drand48 ();
      float ddlogbeta = drand48 ();

      float logp = nominal.logprating (rating, 
                                       0,
                                       exp (data[i].inputs.logbeta),
                                       dlogbeta,
                                       ddlogbeta);

      assert (fabs (logp - data[i].outputs.logp) <=
              1e-4 * (1 + fabs (logp) + fabs (data[i].outputs.logp)) ||
              (std::cerr << logp << " ?= " << data[i].outputs.logp << std::endl,
               0));

      assert (fabs (dlogbeta - data[i].outputs.dlogbeta) <=
              1e-4 * (1 + fabs (dlogbeta) + fabs (data[i].outputs.dlogbeta)) ||
              (std::cerr << dlogbeta << " ?= " << data[i].outputs.dlogbeta << std::endl,
               0));

      assert (fabs (ddlogbeta - data[i].outputs.ddlogbeta) <=
              1e-4 * (1 + fabs (ddlogbeta) + fabs (data[i].outputs.ddlogbeta)) ||
              (std::cerr << ddlogbeta << " ?= " << data[i].outputs.ddlogbeta << std::endl,
               0));
    }
}

struct UpdateTestDatum 
{ 
  struct
    {
      float             alpha[4 * 5 * 5];
      float             logbeta;
      float             pz[5];
      Rating            rating;
    }                                   inputs;
  struct
    {
      float             dalpha[4 * 5 * 5];
    }                                   outputs;
};

void
test_update (void)
{
  UpdateTestDatum data[] = 
    {{{{-1., 0.126693, 0.987885, 0.0993531, 0.237881, 0.214844, -1.,  0.51304, 0.916358, 0.46558, 0.549414, 0.837766, -1., 0.83398,  0.243607, 0.658593, 0.583613, 0.186052, -1., 0.257687, 0.824183,  0.124436, 0.182137, 0.197904, -1., -1., 0.475697, 0.843506,  0.0767013, 0.46014, 0.349004, -1., 0.855621, 0.977348, 0.222259,  0.13416, 0.342581, -1., 0.06099, 0.756679, 0.584746, 0.504815,  0.22701, -1., 0.513072, 0.926153, 0.921202, 0.0409579,  0.255385, -1., -1., 0.10197, 0.796766, 0.858821, 0.0574811,  0.626273, -1., 0.95326, 0.78212, 0.597341, 0.27727,  0.0976391, -1., 0.804772, 0.375082, 0.14311, 0.755058,  0.743782, -1., 0.618403, 0.558364, 0.250243, 0.516772,  0.105331, -1., -1., 0.632211, 0.329041, 0.475814, 0.849947,  0.53024, -1., 0.532275, 0.616993, 0.792465, 0.903967,  0.579015, -1., 0.834873, 0.195125, 0.626697, 0.481376,  0.0301015, -1., 0.820043, 0.483588, 0.726318, 0.28632,  0.20164, -1.},  0.925224, {0.476075, 0.769548, 0.0963083, 0.293013, 0.147034}, {2,  0}}, {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0, -0.157946, 0.0647242, 0.0112184,  0.00959297, 0.0724108, -1.91064, 1.84389, 0.0133654, 0.0205808,  0.0328007, -0.234465, 0.0133196, 0.212292, 0.00223792,  0.00661546, -0.701416, 0.00805099, 0.00828327, 0.673717,  0.0113645, -0.36453, 0.0138219, 0.00705654, 0.0199213, 0.32373, 0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0}}}, {{{-1., 0.293734, 0.246362, 0.762773, 0.614759,  0.676741, -1., 0.453896, 0.858806, 0.0357441, 0.841868,  0.258772, -1., 0.232109, 0.554368, 0.811767, 0.438729,  0.748521, -1., 0.82805, 0.525447, 0.237089, 0.823298,  0.351975, -1., -1., 0.755898, 0.140781, 0.530284, 0.204941,  0.462164, -1., 0.894419, 0.767511, 0.590182, 0.785423,  0.440523, -1., 0.908705, 0.554438, 0.943555, 0.181751,  0.676597, -1., 0.0000695109, 0.131789, 0.743022, 0.928075,  0.172019, -1., -1., 0.606342, 0.505933, 0.104778, 0.820045,  0.850444, -1., 0.365152, 0.574493, 0.615104, 0.388279,  0.470733, -1., 0.806982, 0.024922, 0.602856, 0.0302102,  0.898277, -1., 0.470484, 0.659301, 0.848459, 0.22168,  0.470415, -1., -1., 0.527512, 0.105437, 0.293605, 0.298395,  0.92117, -1., 0.599504, 0.188827, 0.478351, 0.0707268,  0.234351, -1., 0.614333, 0.863247, 0.682448, 0.763618,  0.807351, -1., 0.838325, 0.0795916, 0.733408, 0.909075,  0.367841, -1.},  0.420291, {0.884949, 0.687395, 0.897426, 0.892778, 0.779513}, {1,  3}}, {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0.896876, 0.061913, 0.157936, -1.25996, 0.143238,  0.0855553, 0.79248, 0.0443049, -0.992745, 0.0704052, 0.0679963,  0.114954, 1.03028, -1.30988, 0.096651, 0.0465911, 0.148589,  0.0699532, -0.461066, 0.195933, 0.144126, 0.0568346,  0.0428806, -1.05117, 0.807328, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}}, {{{-1.,  0.39379, 0.599031, 0.971608, 0.180009, 0.204964, -1., 0.12068,  0.900881, 0.945658, 0.59063, 0.257433, -1., 0.218434, 0.182039,  0.783279, 0.419107, 0.138842, -1., 0.448631, 0.874204, 0.0512664,  0.718551, 0.563682, -1., -1., 0.186809, 0.15384, 0.825773,  0.784169, 0.793019, -1., 0.55481, 0.854165, 0.60416, 0.588055,  0.43413, -1., 0.953283, 0.658503, 0.997425, 0.176697,  0.73485, -1., 0.476464, 0.214146, 0.75759, 0.596008,  0.0278327, -1., -1., 0.339942, 0.706324, 0.877456, 0.464151,  0.153133, -1., 0.552483, 0.0516833, 0.679982, 0.360114,  0.997674, -1., 0.197519, 0.0758213, 0.772058, 0.563544,  0.244235, -1., 0.417319, 0.774633, 0.386846, 0.509385,  0.940855, -1., -1., 0.560487, 0.629256, 0.913378, 0.913022,  0.220545, -1., 0.922933, 0.0359216, 0.448871, 0.0674126,  0.37045, -1., 0.984238, 0.76889, 0.707299, 0.372776, 0.78672, -1.,  0.693068, 0.93524, 0.809232, 0.542485, 0.27575, -1.},  0.160607, {0.422386, 0.0330993, 0.334895, 0.60012, 0.79313}, {1,  0}}, {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, -0.211983, 0.0704829, 0.073265, 0.0332842,  0.0349506, -0.0358086, 0.0251024, 0.00404415, 0.00284556,  0.00381646, -0.354779, 0.0460815, 0.248241, 0.0250484,  0.0354084, -0.663857, 0.107001, 0.0555588, 0.426045,  0.0752522, -0.80832, 0.0649742, 0.0785491, 0.153069, 0.511728, 0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0}}}};

  for (unsigned int i = 0; i < sizeof (data) / sizeof (data[0]); ++i)
    {
      for (unsigned int j = 0; j < 4; ++j)
        {
          for (unsigned int k = 0; k < 5; ++k)
            {
              data[i].inputs.alpha[j * 5 * 5 + k * 5 + k] = drand48 ();
              data[i].outputs.dalpha[j * 5 * 5 + k * 5 + k] = 0;
            }
        }

      std::vector<float> alpha (data[i].inputs.alpha,
                                data[i].inputs.alpha + 4 * 5 * 5);
      std::vector<float> gamma (4 * 5 * 5, 0);
      Nominal nominal (&alpha[0], &gamma[0], 0, 4, 5);

      std::vector<float> pz (data[i].inputs.pz, data[i].inputs.pz + 5);

      nominal.update (1,
                      1,
                      0,
                      exp (data[i].inputs.logbeta),
                      pz,
                      data[i].inputs.rating);

      for (unsigned int j = 0; j < 4 * 5 * 5; ++j)
        {
          alpha[j] -= data[i].inputs.alpha[j];

          assert (fabs (alpha[j] - data[i].outputs.dalpha[j]) <=
                  1e-4 * (1 + fabs (alpha[j]) + fabs (data[i].outputs.dalpha[j])) ||
                  (std::cerr << alpha[j] << " ?= " << data[i].outputs.dalpha[j] << std::endl,
                   0));
        }
    }
}

}

int 
main (void) 
{
  srand48 (69);

  test_logprating ();
  test_update ();

  return 0;
}
